﻿using System;
using System.Collections.Concurrent;
using System.Reflection;

namespace Extensions.Attributes
{
    public static class AttributeExtensions
    {
        private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// 获取Attribute的值
        /// </summary>
        /// <typeparam name="TAttribute"></typeparam>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="sourceType"></param>
        /// <param name="attributeValueAction"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static TReturn GetAttributeValue<TAttribute, TReturn>(
            this Type sourceType,
            Func<TAttribute, TReturn> attributeValueAction,
            string propertyName = null) where TAttribute : Attribute
        {
            return GetCachedAttributeValue(sourceType, attributeValueAction, propertyName);
        }


        private static TReturn GetCachedAttributeValue<TAttribute, TReturn>(Type sourceType, Func<TAttribute, TReturn> attributeValueAction,
            string propertyName) where TAttribute : Attribute
        {
            var cacheKey = BuildKey<TAttribute>(sourceType, propertyName);
            var value = Cache.GetOrAdd(cacheKey, k => GetValue(sourceType, attributeValueAction, propertyName));
            if (value is TReturn)
            {
                return (TReturn)Cache[cacheKey];
            }
            return default(TReturn);
        }


        private static TReturn GetValue<TAttribute, TReturn>(
            Type sourceType,
            Func<TAttribute, TReturn> attributeValueAction,
            string propertyName)
            where TAttribute : Attribute
        {
            TAttribute attribute = default(TAttribute);
            var memberInfo = attribute as MemberInfo;
            if (!string.IsNullOrEmpty(propertyName))
            {
                var propertyInfo = sourceType.GetProperty(propertyName);
                if (propertyInfo == null)
                {
                    var fieldInfo = sourceType.GetField(propertyName);
                    if (fieldInfo != null)
                    {
                        memberInfo = fieldInfo;
                    }
                }
                else
                {
                    memberInfo = propertyInfo;
                }
            }
            attribute = memberInfo.GetCustomAttribute<TAttribute>(false);
            return attribute == null ? default(TReturn) : attributeValueAction(attribute);
        }

        /// <summary>
        /// 生成Cache的Key
        /// </summary>
        /// <typeparam name="TAttribute"></typeparam>
        /// <param name="sourceType"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private static string BuildKey<TAttribute>(Type sourceType, string propertyName)
        {
            var attributeName = typeof(TAttribute).FullName;
            if (string.IsNullOrEmpty(propertyName))
            {
                return $"{sourceType.FullName}.{attributeName}";
            }
            return $"{sourceType.FullName}.{propertyName}.{attributeName}";
        }
    }
}
